using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using OpenWaves.Pagination;

namespace OpenWaves.UnitTests
{
    [TestFixture]
    public class PaginateExtensionTests
    {
        [Test]
        public void When_collection_is_empty_Paginate_returns_empty_Enumerable()
        {
            var collection = new int[] {};

            var pages = collection.Paginate(10);

            Assert.That(pages, Is.Empty);
        }

        [Test]
        public void When_collection_count_is_multiplication_of_pageSize_Paginate_returns_correct_number_of_pages()
        {
            var collection = Enumerable.Repeat(1, 10);

            var pages = collection.Paginate(2);

            Assert.That(pages.Count(), Is.EqualTo(5));            
        }

        [Test]
        public void When_collection_count_is_not_multiplication_of_pageSize_Paginate_returns_correct_number_of_pages()
        {
            var collection = Enumerable.Repeat(1, 9);

            var pages = collection.Paginate(2);

            Assert.That(pages.Count(), Is.EqualTo(5));
        }

        [Test]
        public void When_enumerating_page_items_correct_ranges_are_returned()
        {
            var collection = new [] {0, 1, 2, 3, 4};

            var pages = collection.Paginate(2);

            var firstPage = pages.First();
            Assert.That(firstPage.FirstItemIndex, Is.EqualTo(0));
            Assert.That(firstPage.LastItemIndex, Is.EqualTo(1));
            Assert.That(firstPage.GetItemsRange(), Is.EquivalentTo(new[] { 0, 1 }));

            var secondPage = pages.ElementAt(1);
            Assert.That(secondPage.FirstItemIndex, Is.EqualTo(2));
            Assert.That(secondPage.LastItemIndex, Is.EqualTo(3));
            Assert.That(secondPage.GetItemsRange(), Is.EquivalentTo(new[] { 2, 3 }));

            var lastPage = pages.ElementAt(2);
            Assert.That(lastPage.FirstItemIndex, Is.EqualTo(4));
            Assert.That(lastPage.LastItemIndex, Is.EqualTo(4));
            Assert.That(lastPage.GetItemsRange(), Is.EquivalentTo(new[] { 4 }));
        }

        [Test]
        public void Qiven_collection_is_IQuerable_When_enumerating_pages_returned_from_Paginate_collection_is_not_enumerated()
        {
            IQueryable<int> underlyingEnumerable = new EnumerableQuery<int>(new[] { 0, 1, 2 });

            var collectionMock = new Mock<IQueryable<int>>();
            collectionMock.Setup(c => c.Provider).Returns(underlyingEnumerable.Provider);
            collectionMock.Setup(c => c.Expression).Returns(underlyingEnumerable.Expression);
            collectionMock.Setup(c => c.GetEnumerator()).Throws(new AssertionException("Attempted to enumerate the collection."));

            var collection = collectionMock.Object as IEnumerable<int>;

            var pages = collection.Paginate(2);

            // ToList() will enumerate pages collection.
            pages.ToList();
        }

        [Test]
        public void Given_collection_is_IList_When_enumerating_pages_returned_from_Paginate_collection_is_not_enumerated()
        {
            var collectionMock = new Mock<IList<int>>();
            collectionMock.Setup(c => c.Count).Returns(10);
            collectionMock.Setup(c => c.GetEnumerator()).Throws(new AssertionException("Attempted to enumerate the collection."));

            var pages = collectionMock.Object.Paginate(2);

            // ToList() will enumerate pages collection.
            pages.ToList();
        }

        [Test]
        public void Given_LazyCollection_When_enumerating_pages_returned_from_Paginate_GetRange_is_not_called()
        {
            var collectionMock = new Mock<ILazyCollection<int>>();
            collectionMock.Setup(c => c.Count).Returns(10);
            collectionMock.Setup(c => c.GetRange(0, 0)).Throws(new AssertionException("Attempted to call ILazyCollection.GetRange(...)."));

            var pages = collectionMock.Object.Paginate(2);

            // ToList() will enumerate pages collection.
            pages.ToList();
        }

        [Test]
        public void Given_non_empty_PageCollection_When_calling_GetRange_exceeding_pages_count_Then_all_pages_are_returned()
        {
            var items = Enumerable.Range(0, 15);
            
            var pages = items.Paginate(5);
            const int expectedCount = 3;

            // Ask for 4 pages - actually only 3 there
            var actualCount = pages.GetRange(0, 4).Count();

            Assert.That(actualCount, Is.EqualTo(expectedCount));
        }
    }
}